#ifndef FILE_DIALOG_H
#define FILE_DIALOG_H

#include "list.h"
#include "textbox.h"
#include "button.h"

#include <QString>
#include <QStringList>
#include <QDir>
#include <QStack>

class FileDialog;

class FileDialogListener
{
public:
    virtual void OnSave(FileDialog* sender, QString absolutePath, QString chosenName) = 0;
    virtual void OnOpen(FileDialog* sender, QString filename) = 0;
};

class FileDialog: public Widget, private ButtonListener, private ListListener
{
public:
    enum Mode {Open, Save};

    FileDialog(Widget* _parent, Engine* _engine, int _x, int _y, int _w, int _h,
               MainWidget* _mw, Mode _mode = Open, const QString& initDir = ".")
                   :Widget(_parent, _engine, _x, _y, _w, _h),
                   topBarHeight(25),
                   textBoxHeight(30),
                   dir(initDir),
                   topbar(w - 2 * topBarHeight, topBarHeight + 1)
    {
        mode = _mode;
        foldersList = new List(this, engine, 0, topBarHeight, w/2, h - topBarHeight - textBoxHeight - 15,
                               topBarHeight, List::Left);
        foldersList->AddListListener(this);

        filesList = new List(this, engine, w/2, topBarHeight, w/2, foldersList->GetHeight(), topBarHeight);
        filesList->AddListListener(this);

        tBox = new TextBox(this, engine, 0, h - textBoxHeight, w - 100, textBoxHeight, _mw);
        tBox->GetTextBox()->setEnabled(mode == Save);

        button = new Button(this, engine, mode==Open?"Open":"Save", w - 90, h - textBoxHeight, 90, textBoxHeight);

        AddChild(foldersList);
        AddChild(filesList);
        AddChild(tBox);
        AddChild(button);
        button->AddButtonListener(this);

        DrawTopBar();

        hideTopbar = false;
        nowHidden = false;
        showingDrives = false;
        emptyTextBox = false;
        history.push(dir.absolutePath());
        nameFilters << "*.*";
        RefreshState();

        mouseTracking = true;
        updateTextBoxStatus = false;
    }

    virtual ~FileDialog(){}

    // paint
    virtual void Paint(QPainter &painter)
    {
        if(nowHidden) return;
        if(hideTopbar)
        {
            topbar.fill(QColor(0, 0, 0, 0));
            painter.drawPixmap(x + topBarHeight, y, topbar, 0, 0, topbar.width(), topbar.height());

            painter.setPen(QColor(255, 255, 255, 255));
            painter.setBrush(QBrush(QColor(0, 0, 0, 0), Qt::SolidPattern));
            painter.drawRect(x + 2 * topBarHeight, y, w - 4 * topBarHeight, topBarHeight);
            hideTopbar = false;
            nowHidden = true;
            Hidden();
            return;
        }else
        {
            if(emptyTextBox)
            {
                emptyTextBox = false;
                tBox->SetText("");
            }
            painter.drawPixmap(x + topBarHeight, y, topbar, 0, 0, topbar.width(), topbar.height());
            painter.drawText(x + 2 * topBarHeight + 7, y + topBarHeight - topBarHeight/4,
                         showingDrives? "drives" : dir.absolutePath());
        }
    }

    // visibility handling
    virtual void Hide()
    {
        if(!visible)
        {
            Hidden();
            return;
        }

        hideTopbar = true;
        nowHidden = false;

        if(children.size() > 0)
        {
            eventCount = children.size();
            for(vector< Widget* >::iterator i=children.begin(); i!=children.end(); ++i)
            {
                (*i)->Hide();
            }
        } else
        {
            doHide();
        }
    }

    virtual void doShow()
    {
        visible = true;
        paintEnabled = true;
        hideTopbar = false;
        nowHidden = false;
        DrawTopBar();

        Showed();
    }

    virtual void doHide()
    {
        visible = false;
        /*
        hideTopbar = true;
        nowHidden = false;
        */
    }

    // ButtonListener implementation
    virtual void OnMouseClick(QMouseEvent *e, int x_rel, int y_rel)
    {
        // topbar pick correlation

        // up dir
        QRect topbarLeftRect(topBarHeight, 0, topBarHeight, topBarHeight);
        if(topbarLeftRect.contains(x_rel, y_rel, true))
        {
            if(!showingDrives)
            {
                if(dir.cdUp())
                {
                    history.push(dir.absolutePath());
                    RefreshState();
                }else
                {
                    showingDrives = true;
                    RefreshState();
                }
            }
            return;
        }

        // back
        QRect topbarRightRect(w - 2 * topBarHeight, 0, topBarHeight, topBarHeight);
        if(topbarRightRect.contains(x_rel, y_rel, true))
        {
            QString historyTop = history.pop();
            if(history.isEmpty())
            {
                history.push(historyTop);
            }else
            {
                dir.cd(history.top());
                RefreshState();
            }
            return;
        }
    }

    // listeners handling
    void AddFileDialogListener(FileDialogListener* fdl)
    {
        listeners.append(fdl);
    }

    // name filters handling
    void SetNameFilters(QStringList& filters)
    {
        nameFilters = filters;
        RefreshState();
    }

    // dir
    void SetDir(const QString& newDir)
    {
        dir.cd(newDir);
        RefreshState();
    }

private:
    int topBarHeight;
    int textBoxHeight;

    QDir dir;
    QStringList folders;
    QStringList files;
    QStack< QString > history;
    Mode mode;

    List* foldersList;
    List* filesList;
    TextBox* tBox;
    Button* button;
    QPixmap topbar;

    QList< FileDialogListener* > listeners;
    QStringList nameFilters;

    bool updateTextBoxStatus;
    bool emptyTextBox;

    bool showingDrives;

    bool hideTopbar;
    bool nowHidden;

    void DrawTopBar()
    {
        topbar.fill(QColor(0, 0, 0, 0));
        QPainter p(&topbar);
        p.setPen(QColor(0, 0, 0, 255));
        p.setBrush(QBrush(QColor(255, 255, 255, 0), Qt::NoBrush));
        p.drawRect(0, 0, topbar.width() - 1, topbar.height());
        p.drawLine(topBarHeight, 0, topBarHeight, topBarHeight);
        p.drawLine(topbar.width() - topBarHeight - 1, 0, topbar.width() - topBarHeight - 1, topBarHeight);

        p.setBrush(QBrush(QColor(69, 27, 139, 150), Qt::SolidPattern));
        QPoint points[3] = {
            QPoint(topBarHeight/4, topBarHeight/2),
            QPoint(topBarHeight - topBarHeight/4, topBarHeight/4),
            QPoint(topBarHeight - topBarHeight/4, topBarHeight - topBarHeight/4)
        };
        p.drawPolygon(points, 3);

        int wi = topbar.width();
        points[0] = QPoint(wi - topBarHeight + topBarHeight/4, topBarHeight/4);
        points[1] = QPoint(points[0].x(), topBarHeight - topBarHeight/4);
        points[2] = QPoint(wi - topBarHeight/4, topBarHeight/2);
        p.drawPolygon(points, 3);
    }

    void RefreshState()
    {
        if(!showingDrives)
        {
            folders = dir.entryList(QDir::Dirs | QDir::Drives | QDir::NoDotAndDotDot, QDir::Name);
            files = dir.entryList(nameFilters, QDir::Files);
            foldersList->SetStringList(folders);
            filesList->SetStringList(files);
        }else
        {
            QFileInfoList drives = QDir::drives();

            QStringList drivesList;
            for(int i=0; i<drives.size(); i++)
            {
                drivesList.append(drives.at(i).absolutePath());
            }
            foldersList->SetStringList(drivesList);

            QStringList empty;
            filesList->SetStringList(empty);
        }
        emptyTextBox = true;
    }

    // ButtonListener implementation
    virtual void OnClick(Button* b, QMouseEvent* e, int rel_x, int rel_y)
    {
        if(mode == Open)
        {
            if(filesList->GetSelectedIndex() != -1)
            {
                QString fPath = dir.absolutePath() + QDir::separator();
                QString fName = QDir::toNativeSeparators(fPath) + filesList->GetSelectedString();
                for(int i=0; i<listeners.size(); i++)
                {
                    listeners.at(i)->OnOpen(this, fName);
                }
            }
        }else
        {
            if(!(tBox->GetTextBox()->toPlainText().isEmpty()))
            {
                QString fPath = QDir::toNativeSeparators(dir.absolutePath() + QDir::separator());
                for(int i=0; i<listeners.size(); i++)
                {
                    listeners.at(i)->OnSave(this, fPath, tBox->GetTextBox()->toPlainText());
                }
            }
        }
    }

    // ListListener implementation
    virtual void OnSelect(List* sender, int index)
    {
        if(sender == filesList)
        {
            tBox->SetText(filesList->GetSelectedString());
        }
    }

    virtual void OnDoubleClick(List *sender, int index)
    {
        if(sender == foldersList)
        {
            if(dir.cd(foldersList->GetSelectedString()))
            {
                showingDrives = false;
                history.push(dir.absolutePath());
                RefreshState();
            }
        }
        else    // double click on file
        {
            OnClick( 0, 0, 0, 0 );
        }
    }
};

#endif // FILE_DIALOG_H
